﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UMLSynthesizer.LangAnalyzer.Elements;
using UMLSynthesizer.LangAnalyzer.Expressions;

namespace UMLSynthesizer.LangAnalyzer.ExtensionMethods
{
    public static class ExpressionExtension
    {
        /// <summary>
        /// Return the list of used types
        /// </summary>
        /// <param name="expression">The expression to analyze</param>
        /// <returns>the list of types</returns>
        public static IEnumerable<ITypeBase> GetUsedTypes(this IExpressionBase expression)
        {
            List<ITypeBase> typeUsedList = new List<ITypeBase>();

            if (expression as IArrayCreationExpression != null)
            {
                typeUsedList.AddRange((expression as IArrayCreationExpression).Initializer.GetUsedTypes());
                typeUsedList.Add((expression as IArrayCreationExpression).Type);
            }
            if (expression as IBinaryExpression != null)
            {
                typeUsedList.AddRange((expression as IBinaryExpression).LeftExpression.GetUsedTypes());
                typeUsedList.AddRange((expression as IBinaryExpression).RightExpression.GetUsedTypes());
            }
            if (expression as ICastExpression != null)
            {
                typeUsedList.AddRange((expression as ICastExpression).Expression.GetUsedTypes());
                typeUsedList.Add((expression as ICastExpression).Type);
            }
            if (expression as IConditionalExpression != null)
            {
                typeUsedList.AddRange((expression as IConditionalExpression).Condition.GetUsedTypes());
                typeUsedList.AddRange((expression as IConditionalExpression).WhenFalse.GetUsedTypes());
                typeUsedList.AddRange((expression as IConditionalExpression).WhenTrue.GetUsedTypes());
            }
            if (expression as IElementReferenceExpression != null &&
                (expression as IElementReferenceExpression).ReferredElement as ITypeBase != null)
                typeUsedList.Add((expression as IElementReferenceExpression).ReferredElement as ITypeBase);
            if (expression as IGenericExpression != null)
                typeUsedList.AddRange((expression as IGenericExpression).ReferredTypes);
            if (expression as IInitializerExpression != null)
                typeUsedList.AddRange((expression as IInitializerExpression).Expressions
                    .SelectMany(expr => expr.GetUsedTypes()));
            if (expression as IInvocationExpression != null)
            {
                typeUsedList.AddRange((expression as IInvocationExpression).ArgumentList
                    .SelectMany(expr => expr.GetUsedTypes()));
                typeUsedList.AddRange((expression as IInvocationExpression).Expression.GetUsedTypes());
            }
            if (expression as IMemberAccessExpression != null)
                typeUsedList.AddRange((expression as IMemberAccessExpression).Expression.GetUsedTypes());
            if (expression as IObjectCreationExpression != null)
            {
                typeUsedList.AddRange((expression as IObjectCreationExpression).ArgumentList
                    .SelectMany(expr => expr.GetUsedTypes()));
                typeUsedList.AddRange((expression as IObjectCreationExpression).Initializer.GetUsedTypes());
                typeUsedList.Add((expression as IObjectCreationExpression).Type);
                typeUsedList.AddRange((expression as IObjectCreationExpression).TypeArgumentList);
            }
            if (expression as IParenthesizedExpression != null)
                typeUsedList.AddRange((expression as IParenthesizedExpression).Expression.GetUsedTypes());
            if (expression as IPostfixUnaryExpression != null)
                typeUsedList.AddRange((expression as IPostfixUnaryExpression).Operand.GetUsedTypes());
            if (expression as IPrefixUnaryExpression != null)
                typeUsedList.AddRange((expression as IPrefixUnaryExpression).Operand.GetUsedTypes());
            if (expression as ISizeOfExpression != null)
                typeUsedList.Add((expression as ISizeOfExpression).Type);

            return typeUsedList.Distinct().Where(type => type != null);
        }
    }
}
